﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// NotReviewedButNeeded
#pragma once

#include "NdbObjectContainer.h"
#include "NdbObjectArchiveHelper.h"
#include "NdbPropertySerialize.h"
#include "NdbReplaceElementIdHelper.h"
#include "NdbReportElementIdHelper.h"
#include "NdbCopyHelper.h"
#include "IIOStream.h"
#include "NdbObjectMemoryModelGroupNode.h"
#include "NdbArchiveBlockHeader.h"
#include <set>

namespace gcmp
{
    class IDbObjectArchive;

    /// \brief Ndb集合
    ///
    /// 对std::set的封装，支持Ndb的序列化。暂不支持和std::set一样指定第三个模板参数Allocator。
    /// \see std::set
    template <
        typename T,
        typename P = std::less<T>
    >
        class NdbSet : public NdbObjectContainer
    {
        typedef std::true_type is_set;
    public:
        /// \brief 默认构造函数，构造空容器
        explicit NdbSet() {}

        /// \brief 拷贝另一个std::set容器的内容进行构造
        /// \param data 另一个std::set容器
        NdbSet(const std::set<T, P>& data) : m_data(data) {}

        /// \brief 使用move语义，用另一个std::set容器内容进行构造
        /// \param data 另一个std::set容器
        NdbSet(std::set<T, P>&& data) : m_data(std::move(data)) {}

        /// \brief 拷贝构造函数
        /// \param x 另一个NdbSet容器
        NdbSet(const NdbSet& x) : m_data(x.m_data) {}

        /// \brief 使用move语义，用另一个NdbSet容器内容进行构造
        /// \param x 另一个NdbSet容器
        NdbSet(NdbSet&& x) : m_data(std::move(x.m_data)) {}

        /// \brief 析构函数
        ~NdbSet() {}

        /// \brief 赋值操作符
        /// \param x 另一个NdbSet容器
        NdbSet& operator= (const NdbSet& x)
        {
            if (this != &x)
            {
                m_data = x.m_data;
            }
            return *this;
        }

        /// \brief 移动赋值
        /// \param x 另一个NdbSet容器
        NdbSet& operator= (NdbSet&& x)
        {
            if (this != &x)
            {
                m_data = std::move(x.m_data);
            }
            return *this;
        }

#pragma region std::set封装相关函数
        /// \brief 转换操作符，转换成std::set
        ///
        /// 不可修改
        operator const std::set<T, P>& () const
        {
            return m_data;
        }

        /// \brief 转换操作符，转换成std::set
        operator std::set<T, P>& ()
        {
            return m_data;
        }

        /// \brief 获取封装的std::set，不可修改。
        const std::set<T, P>& StdSet() const { return m_data; }

        /// \brief 获取封装的std::set，可修改。
        std::set<T, P>& StdSetFW() { return m_data; }

#pragma endregion

#pragma region Ndb相关函数
        virtual NdbPropertySchemaType GetSchemaType() const override
        {
            return NdbPropertySchemaType::Set;
        }

        virtual OwnerPtr<NdbObjectContainer> NdbClone()const override
        {
            OwnerPtr<NdbSet> opClonedSet = NEW_AS_OWNER_PTR(NdbSet);
            for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
            {
                T k = NdbCopyHelper<T>::Copy(*ite);
                opClonedSet->m_data.insert(std::move(k));
            }
            return TransferOwnership(opClonedSet);
        }

        virtual void NdbAssignByMove(OwnerPtr<NdbObjectContainer> opSrc) override
        {
            NdbSet* pSet = dynamic_cast<NdbSet*>(opSrc.get());
            if (pSet)
            {
                *this = std::move(*pSet);
            }
        }

        virtual UInt64 NdbSerialize(IDbObjectArchive& objectArchive, NdbPropertyUtils::BehavioursAdapter adapter) const override
        {
            UInt64 nSize = m_data.size();
            NdbContainerArchiveBlockHeader containerArchiveBlockHeader(NdbContainerArchiveBlockHeader::s_CurrentVersion, nSize, NdbPropertySchemaType::Set);

            if (property_is_value_type<T>::value)
            {
                NdbArchiveBlockHeader blockHeader = GetSharedBlockHeader<T>();
                containerArchiveBlockHeader.SetSharedHeader(blockHeader, 0);
                UInt64 length = containerArchiveBlockHeader.Serialize(objectArchive.GetIOStream());

                for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
                {
                    length += NdbPropertySerializeWithoutHeader<T>::Serialize(*ite, objectArchive, adapter);
                }
                return length;
            }
            else
            {
                UInt64 length = containerArchiveBlockHeader.Serialize(objectArchive.GetIOStream());
                for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
                {
                    length += NdbPropertySerialize<T>::Serialize(*ite, objectArchive, adapter);
                }
                return length;
            }
        }

        virtual void NdbExtractObjectFromMemory(NdbObjectMemoryModelNode* pObjMemModel, UInt32 majorVersion) override
        {
            NdbObjectMemoryModelGroupNode* pMemGroupNode = thread_safe_quick_cast<NdbObjectMemoryModelGroupNode>(pObjMemModel);
            DBG_WARN_AND_RETURN_VOID_UNLESS(pMemGroupNode, L"pMemGroupNode为空？", L"GMEP", L"2020/11/24");
            std::vector<OwnerPtr<NdbObjectMemoryModelNode>>& subNodes = pMemGroupNode->GetSubNodesFW();
            UInt32 nSize = (UInt32)subNodes.size();
            for (UInt32 i = 0; i < nSize; ++i)
            {
                NdbObjectMemoryModelNode* pSubNode = subNodes[i].get();
                if (!pSubNode) continue;
                T data;
                switch (pSubNode->GetSchemaType())
                {
                case NdbPropertySchemaType::Value:
                    NdbObjectArchiveHelper::GetValueFromMemoryModel(&data, pSubNode);
                    break;
                case NdbPropertySchemaType::ElementId:
                    NdbObjectArchiveHelper::GetElementIdFromMemoryModel(&data, pSubNode);
                    break;
                case NdbPropertySchemaType::WString:
                    NdbObjectArchiveHelper::GetWStringFromMemoryModel(&data, pSubNode);
                    break;
                case NdbPropertySchemaType::SchematicObject:
                    NdbObjectArchiveHelper::GetSchematicObjectFromMemoryModel(&data, pSubNode, false/*isParentSubObject*/, majorVersion);
                    break;
                case NdbPropertySchemaType::CloneableObject:
                    NdbObjectArchiveHelper::GetCloneableObjectFromMemoryModel(&data, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::OwnerPtrToNdbObject:
                    NdbObjectArchiveHelper::GetOwnerPtrFromMemoryModel(&data, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::OwnerPtrToIDbObject:
                    NdbObjectArchiveHelper::GetOwnerPtrToIDbObjectFromMemoryModel(&data, pSubNode, majorVersion);
                    break;
                case NdbPropertySchemaType::NativePointer:
                    NdbObjectArchiveHelper::GetPointerFromMemoryModel(&data, pSubNode);
                    break;
                default:
                    DBG_WARN(L"NdbSet: 不支持存放的类型" + NdbPropertyUtils::SchemaTypeAsWString(pSubNode->GetSchemaType()), L"GMEP", L"2017-01-16");
                    break;
                }
                m_data.insert(m_data.end(), std::move(data));
            }
        }
#pragma endregion

#pragma region ElementId相关函数
        virtual bool HasElementIdProperty() const override
        {
            if (m_data.empty()) return false;

            // T的类型是OwnerPtr时，不能通过第一个元素确定。详见ModelIdDetectorHelper的OwnerPtr特化版本说明
            auto ite = m_data.begin();
            return ElementIdDetectorHelper<T>::DetectElementId(&(*ite));
        }

        virtual void ReplaceElementIds(const std::unordered_map<ElementId, ElementId, ElementIdHash>& oldToNewMap) override
        {
            if (!HasElementIdProperty()) return;

            std::set<T, P> set;
            for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
            {
                T value = NdbCopyHelper<T>::Copy(*ite);
                ReplaceElementIdHelper<T>::ReplaceElementId(&value, oldToNewMap);
                set.insert(std::move(value));
            }
            m_data.swap(set);
        }

        virtual void ReportElementIds(
            std::set<ElementId>* pModelIds,
            std::unordered_map<ElementId, std::vector<std::wstring>, ElementIdHash>* pPaths,
            std::vector<std::wstring>* pCurrentPath
        ) const override
        {
            DBG_WARN_AND_RETURN_VOID_UNLESS(pModelIds, L"pModelIds不应该为空", L"GMEP", L"2016/10/14");
            if (!HasElementIdProperty()) return;

            for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
            {
                ReportElementIdHelper<T>::ReportElementId(*ite, pModelIds, pPaths, pCurrentPath);
            }
        }
#pragma endregion

        virtual void CollectModuleGroupsOfNdbObject(std::set<Guid, GuidComparer>& moduleGroupsGuids) const
        {
            for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
            {
                NdbPropertySerialize<T>::CollectModuleGroups(*ite, moduleGroupsGuids);
            }
        }

        virtual void CheckTopOwnerObject(std::vector<const UniqueWString*>& path,const NdbObject* correctowner,
            const NdbClassSchema* pNdbClassSchema, std::vector<std::wstring>& resultstr) const override
        {
            const std::wstring& subpropertyname = UniqueWString::Find(L"")->GetContent();
            std::vector<const UniqueWString*> localpath;
            localpath.insert(localpath.end(), path.begin(), path.end());
            localpath.push_back(UniqueWString::Find(L""));

            for (auto ite = m_data.begin(); ite != m_data.end(); ++ite)
            {
                if (!std::is_base_of<IDbObject, T>::value)
                {
                    auto result = Convert_Type_If<std::is_base_of<NdbObject, T>::value, property_is_owner_ptr_type<T>::value, NdbObject, T>::GetData((*ite));
                    NdbObject* pObject = GetNonConstPoint<NdbObject*>::GetNonConstPointImp(result);

                    if (pObject != nullptr)
                    {
                        pObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);

                    }

                }
                else
                {
                    auto result = Convert_Type_If<std::is_base_of<IDbObject, T>::value, property_is_owner_ptr_type<T>::value, IDbObject, T>::GetData((*ite));
                    IDbObject* pPropertyIDbObject = GetNonConstPoint<IDbObject*>::GetNonConstPointImp(result);
                    if (pPropertyIDbObject != nullptr)
                    {
                        NdbObject* pPropertyNDbObject = dynamic_cast<NdbObject*>(pPropertyIDbObject);
                        if (pPropertyNDbObject == nullptr)
                            continue;

                        //FYI(L"log test:"+subpropertyname)
                        pPropertyNDbObject->CheckTopOwnerObject(localpath, correctowner, nullptr, resultstr);
                    }

                }
            }

        }

    private:
        std::set<T, P> m_data;


    };
}
